package main

import (
    "../medusa"
    "fmt"
    //"container/list"
    "../msgcode"
)

/*
 import(
 "medusa"
 )*/

//==============================================================
//Acceptor: Client
//==============================================================

type Client struct {
    medusa.TcpAcceptor

    pack_idle *medusa.Packet
}

func NewClient(listener medusa.Listener, uuid int) *Client {

    acc := new(Client)
    acc.Constructor(listener, uuid)

    return acc
}

func (self *Client) Constructor(listener medusa.Listener, uuid int) {
    self.pack_idle = medusa.NewPacket(msgcode.IN_ECHO)
    self.TcpAcceptor.Constructor(listener, uuid,
        //event callback: OnIdle        
        func() {
            self.SendPacket(self.pack_idle)
        },

        //event callback: PreOnSent    
        func(pack *medusa.Packet) {

        },

        //event callback: ConnectionLose
        func() {
            self.Stack("Event_ConnectionLose---------in")
            //通知world有玩家掉线
            pack := medusa.NewPacket(msgcode.IN_CLIENT_DISCONNECT)
            pack.WriteInt32LE(self.GetUUID32())
            Global().SendToWorld(pack)
            self.Warning("ConnectionLose: (uuid:<%d>: %v <+ %v)", self.GetUUID32(), self.GetLocalAddr(), self.GetRemoteAddr())

            self.Stack("Event_ConnectionLose---------out")
        },

        //event callback: UnknowPacketReceived 
        func(pack *medusa.Packet) {
            self.Stack("Event_UnknowPacketReceived---------in:des%v", pack.Des)

            if pack.Check() {
                pack.SetSessionMode(self.GetUUID32())
                //self.Stack("Client==>World: des:%v(uuid:<%d>: %v <+ %v)", pack.Des, client.GetUUID32(), client.GetLocalAddr(), client.GetRemoteAddr())
                Global().SendToWorld(pack)
            }
            self.Stack("Event_UnknowPacketReceived---------out")
        })  // end: Constructor)

    //init methods
    self.init_methods()

    //init protocol
    self.init_protocol()
}

//--------------------------------------------------------------
//Client: methods
//--------------------------------------------------------------
//Client: internal implements
func (self *Client) init_methods() {

    self.AddMethod("add", func(args ...interface{}) (result interface{}, err error) {
        a := args[0].(int)
        b := args[1].(int)
        result = a + b
        return
    })

    //your code

}

//Client: external inteface
func (self *Client) Add(a, b int) {
    c, err := self.CallMethod("add", a, b)
    if err == nil {
        fmt.Printf("%v + %v = %v\n", a, b, c.(int))
    } else {
        fmt.Printf(err.Error())
    }

}

//--------------------------------------------------------------
//Client: network protocol
//--------------------------------------------------------------
func (self *Client) init_protocol() {
    self.AddProtocol(msgcode.IN_ECHO, func(pack *medusa.Packet) {
        //self.Stack("Wrold.IN_ECHO---------in")
        self.Success("remote is alive! (uuid:<%d>: %v <- %v)", self.GetUUID32(), self.GetLocalAddr(), self.GetRemoteAddr())
        //self.Stack("World.IN_ECHO---------out")
    })
}

//==============================================================
//Listener: MyListener
//==============================================================
type GateService struct {
    medusa.TcpListener

    uuid_index int

    spaces     map[int32]*Space
    all_client map[int32]medusa.Acceptor
}

func NewGateService(name string, max_conn int,
    rcv_buf int, snd_buf int, sndchan int, max_idle_sec int64, log medusa.Log) *GateService {

    listen := new(GateService)
    listen.Constructor(name, max_conn, rcv_buf, snd_buf, sndchan, max_idle_sec, log)

    return listen
}

func (self *GateService) Constructor(name string, max_conn int,
    rcv_buf int, snd_buf int, sndchan int, max_idle_sec int64,
    log medusa.Log) {

    self.spaces = make(map[int32]*Space)
    self.all_client = make(map[int32]medusa.Acceptor)
    self.uuid_index = 1000000
    self.TcpListener.Constructor(name, max_conn, rcv_buf, snd_buf, sndchan, max_idle_sec, log,

        //event callback: gen uuid 

        func() int {
            self.uuid_index = self.uuid_index + 1
            return self.uuid_index
        },

        //event callback: create connection object
        func(uuid int) medusa.Acceptor {
            cli := NewClient(self, uuid)
            return cli
        },

        //event callback: connection made 
        func(acc medusa.Acceptor) {
            self.Stack("Event_ConnectionMade---------in")
            //client := acc.(*Client)
            self.all_client[acc.GetUUID32()] = acc
            self.Success("ConnectionMade: (uuid:<%d>: %v <- %v)\n", acc.GetUUID32(), acc.GetLocalAddr(), acc.GetRemoteAddr())
            self.Stack("Event_ConnectionMade---------out")
        },

        //event callback: connection lose 
        func(acc medusa.Acceptor) {
            delete(self.all_client, acc.GetUUID32())
        })  // end: medusa.NewListener

    //init methods
    self.init_methods()
}

//--------------------------------------------------------------
//GateService: methods
//--------------------------------------------------------------
//GateService: internal implements
func (self *GateService) init_methods() {

    self.AddMethod("add", func(args ...interface{}) (result interface{}, err error) {
        a := args[0].(int)
        b := args[1].(int)
        result = a + b
        return
    })

    //your code

    self.AddMethod("into-space", func(args ...interface{}) (result interface{}, err error) {
        sid := args[0].(int32)
        uid := args[1].(int32)
        if self.spaces[sid] == nil {
            self.spaces[sid] = NewSpace(sid)
        }
        self.spaces[sid].Into(self.all_client[uid])
        return
    })

    self.AddMethod("leave-space", func(args ...interface{}) (result interface{}, err error) {
        sid := args[0].(int32)
        uid := args[1].(int32)
        if self.spaces[sid] != nil {
            self.spaces[sid].Leave(uid)
            if self.spaces[sid].Count() <= 0 {
                delete(self.spaces, sid)
            }
        }
        return
    })

    self.AddMethod("clear-space", func(args ...interface{}) (result interface{}, err error) {
        sid := args[0].(int32)
        delete(self.spaces, sid)
        return
    })

    self.AddMethod("space-broadcast", func(args ...interface{}) (result interface{}, err error) {
        pack := args[0].(*medusa.Packet)
        sid := pack.GetSpaceUUID()
        if self.spaces[sid] != nil {
            self.spaces[sid].Broadcast(pack)
        }
        return
    })
}

//GateService: external inteface
func (self *GateService) Add(a, b int) {
    c, err := self.CallMethod("add", a, b)
    if err == nil {
        fmt.Printf("%v + %v = %v\n", a, b, c.(int))
    } else {
        fmt.Printf(err.Error())
    }

}

func (self *GateService) IntoSpace(space_uuid, client_uuid int32) {
    self.CallMethod("into-space", space_uuid, client_uuid)
}

func (self *GateService) LeaveSpace(space_uuid, client_uuid int32) {
    self.CallMethod("leave-space", space_uuid, client_uuid)
}

func (self *GateService) ClearSpace(space_uuid int32) {
    self.CallMethod("clear-space", space_uuid)
}

func (self *GateService) SpaceBroadcast(pack *medusa.Packet) {

    sid := pack.GetSpaceUUID()
    if sid == 0 {
        self.Broadcast(pack, (int)(pack.GetExceptUUID()))
    } else {
        self.CallMethod("space-broadcast", pack)
    }

}
